package com.tfswx.ruleengine.compute.gzjy;

import com.greenpineyu.fel.Expression;
import com.greenpineyu.fel.compile.InterpreterSourceBuilder;
import com.greenpineyu.fel.compile.SourceBuilder;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.function.Function;
import com.greenpineyu.fel.parser.FelNode;
import com.tfswx.ruleengine.compute.bean.Bds;
import com.tfswx.ruleengine.compute.bean.DataObject;
import com.tfswx.ruleengine.compute.bean.GzjyLog;
import com.tfswx.ruleengine.compute.exception.AppException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 普通函数
 *
 * @author Ricky
 * @date 2021/3/24
 */
@Slf4j
public abstract class BaseFn implements Function {

    protected FelService service = null;
    protected List<String> logs = null;
    // private String id;
    private FelContext context;

    public static Object[] evalArgs(FelNode node, FelContext context) {
        Object[] returnMe = null;
        List<FelNode> children = node.getChildren();
        if (children != null && !children.isEmpty()) {
            Object[] args = children.toArray();
            int size = args.length;
            returnMe = new Object[size];
            System.arraycopy(args, 0, returnMe, 0, size);
            for (int i = 0; i < size; i++) {
                Object child = args[i];
                if (child instanceof Expression) {
                    Expression childExp = ((Expression) child);
                    returnMe[i] = childExp.eval(context);
                }
            }
        }
        return returnMe;
    }

    @Override
    public Object call(FelNode node, FelContext context) {
        this.context = context;
        // 如果参数中包含表达式，执行表达式。将表达式替换成表达式执行结果。
        Object[] children = evalArgs(node, context);
        if (children == null) {
            children = new Object[0];
        }
        int minParamSize = getMinParamSize();
        int maxParamSize = getParams().size();
        if (children.length < minParamSize || children.length > maxParamSize) {
            throw new AppException(getName() + " 参数个数不正确(" + minParamSize + " <= len <= " + maxParamSize + ")");
        }
        boolean isFinish = false;
        Object call = null;
        service = (FelService) context.get("service");
        logs = service.getGzjyLog().getLogs();

        List<Object> params = getFnParamList(children);

        try {
            before();
            call = call(children, context);
            after();
            isFinish = true;
        } catch (Exception e) {
            String errMsg = "";
            StringBuilder param = new StringBuilder();
            for (Object child : params) {
                param.append(",").append(child);
            }
            param.deleteCharAt(0);

            errMsg += getName() + "(" + param + ")";
            throw new AppException(errMsg, e);
        } finally {
            if (service != null) {
                List<GzjyLog.FnLogDetail> fnLogDetailList = service.getGzjyLog().getFnLogDetailList();
                GzjyLog.FnLogDetail fnLogDetail = new GzjyLog.FnLogDetail();
                fnLogDetail.setFfmc(getName());
                fnLogDetail.setFfcs(params);
                fnLogDetail.setFfjg(isFinish ? call : "");
                fnLogDetail.setSfcg(isFinish);
                fnLogDetailList.add(fnLogDetail);
            }
        }
        return call;
    }

    private List<Object> getFnParamList(Object[] children) {
        List<Object> params = new ArrayList<>();
        for (Object child : children) {
            if (child instanceof DataObject) {
                params.add("$data");
            } else if (child instanceof String) {
                params.add("'" + child + "'");
            } else {
                params.add(child);
            }
        }
        return params;
    }

    @Override
    public SourceBuilder toMethod(FelNode node, FelContext ctx) {
        return InterpreterSourceBuilder.getInstance();
    }

    void before() {
        // id = CommonUtil.getUuid();
        // String mc = getZwmc();
        // if (StringUtils.isBlank(mc)) {
        //     mc = getName();
        // }
        // log.info("开始执行：{}-{}", mc, id);
    }

    void after() {
        // String mc = getZwmc();
        // if (StringUtils.isBlank(mc)) {
        //     mc = getName();
        // }
        // log.info("执行完成：{}-{}", mc, id);
    }

    /**
     * 调用函数
     *
     * @param arguments
     * @param context
     * @return Object
     */
    public abstract Object call(Object[] arguments, FelContext context);

    /**
     * 中文名称
     *
     * @return java.lang.String
     * @author Ricky
     * @date 2021/4/15
     */
    public abstract String getZwmc();

    /**
     * 获取属性列表
     *
     * @return List<String>
     */
    public List<Param> getParams() {
        return new ArrayList<>();
    }

    public int getMinParamSize() {
        List<Param> params = getParams();
        if (params == null) {
            return 0;
        }
        int size = 0;
        for (Param param : params) {
            if (Boolean.TRUE.equals(param.required)) {
                size++;
            }
        }
        return size;
    }

    public String getFfStr() {
        List<Param> params = getParams();
        StringBuilder a = new StringBuilder();
        for (int i = 0; i < params.size(); i++) {
            if (i > 0) {
                a.append(", ");
            }
            a.append(params.get(i).getName());
        }
        return getName() + "(" + a + ")";
    }

    public Bds getBds(String bdsName, FelContext context) {
        List<Bds> bdsList = (List<Bds>) context.get("bdsList");
        for (Bds bds : bdsList) {
            if (bds.getBdsmc().equals(bdsName)) {
                return bds;
            }
        }
        throw new AppException("表达式【" + bdsName + "】未定义");
    }

    public DataObject.ZdItemDTO getZd(List<DataObject> akData, String zdm, boolean isDg) {
        if (akData == null) {
            return null;
        }
        String[] split = zdm.split("\\.");
        String stbm = split[0];
        String zdmc = split[1];

        for (DataObject akDatum : akData) {
            if (stbm.equals(akDatum.getStbm())) {
                return findByzdsj(akDatum, zdmc);
            } else {
                if (isDg) {
                    DataObject.ZdItemDTO zd = getZd(akDatum.getCbsj(), zdm, true);
                    if (zd != null) {
                        return zd;
                    }
                }
            }
        }
        return null;
    }

    public DataObject.ZdItemDTO getZd(DataObject parent, String zdm, boolean isDg) {
        if (parent == null) {
            return null;
        }
        String[] split = zdm.split("\\.");
        String stbm = split[0];
        String zdmc = split[1];

        if (stbm.equals(parent.getStbm())) {
            return findByzdsj(parent, zdmc);
        } else {
            if (isDg) {
                DataObject.ZdItemDTO zd = getZd(parent.getParent(), zdm, true);
                if (zd != null) {
                    return zd;
                }
            }
        }
        return null;
    }


    public DataObject.ZdItemDTO getZd(DataObject akData, String zdm) {
        String[] split = zdm.split("\\.");
        String stbm = split[0];
        String zdmc = split[1];

        if (akData != null) {
            // 判断是否为当前表名
            if (stbm.equals(akData.getStbm())) {
                return findByzdsj(akData, zdmc);
            } else {
                // 寻找所有从表
                logs.add("子孙级节点中寻找" + zdm);
                DataObject.ZdItemDTO zd = getZd(akData.getCbsj(), zdm, true);
                if (zd != null) {
                    return zd;
                }
                // 寻找父节点
                DataObject parent = akData.getParent();
                if (parent != null) {
                    logs.add("祖先级节点中寻找" + zdm);
                    zd = getZd(parent, zdm, true);
                    if (zd != null) {
                        return zd;
                    }
                    // 寻找兄弟节点
                    logs.add("兄弟姐妹级节点中寻找" + zdm);
                    zd = getZd(parent.getCbsj(), zdm, false);
                    if (zd == null) {
                        return null;
                    }
                }
            }
        }
        // 不存在主从关系的数据表,从所有数据中查找一个表名匹配的数据
        logs.add("从所有数据中查找一个表名匹配的数据" + zdm);

        List<DataObject> dataObjectList = (List<DataObject>) context.get("dataList");
        Optional<DataObject> first = dataObjectList.stream().filter(item -> item.getStbm().equals(stbm)).findFirst();
        if (!first.isPresent()) {
            return null;
        }
        akData = first.get();

        return findByzdsj(akData, zdmc);
    }

    DataObject.ZdItemDTO findByzdsj(DataObject akData, String zdmc) {
        List<DataObject.ZdItemDTO> zdsj = akData.getZdsj();
        for (DataObject.ZdItemDTO dataFieldDTO : zdsj) {
            if (dataFieldDTO.getZdmc().equals(zdmc)) {
                return dataFieldDTO;
            }
        }
        return null;
    }

    @Data
    public static class Param {
        private String name;
        private String type;
        private Boolean required;

        public Param(String name) {
            this(name, "字符串", true);
        }

        public Param(String name, String type) {
            this(name, type, true);
        }

        public Param(String name, String type, boolean required) {
            this.name = name;
            this.type = type;
            this.required = required;
        }
    }
}

